#ifndef __IO
#define __IO


#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<utility>
#include<sstream>
#include"logger.h"
#include"StandardLibrary.h"
using namespace std;
using namespace Custom;

namespace Stream
{


	class FastOstream
	{
		const int max_size;
		char *buf;
		char ints[20];
		int pos;
		FILE *file;
		inline void writebuf();
		inline void put(char);
		inline void put(char*);
	public:
		FastOstream(FILE*,int);
		~FastOstream();
		inline FastOstream& operator<<(char);
		inline FastOstream& operator<<(const char*);
		template<class T>
		inline FastOstream& operator<<(const T&);
		template<class T>
		inline FastOstream& writeBinary(const T &v,int bytes);
		template<class T>
		inline FastOstream& operator<<(const vector<T> &V);
		template<class T1,class T2>
		inline FastOstream& operator<<(const std::pair<T1,T2> &p);
	};


	template<class T>
	int itoa(T v,char *t)
	{
		int len=0;
		while (v>0)
			t[len++]=v%(T)10+'0',v/=(T)10;
		if (!len)
			t[len++]='0';
		return len-1;
	}

	long long atoi(char *t)
	{
		long long v=0;
		while (*t)
			v*=10 , v+=((*t)-'0') , ++t;
		return v;
	}



	inline void FastOstream::writebuf()
	{
		fwrite(buf,1,pos,file);
		pos=0;
	}

	inline void FastOstream::put(char c)
	{
		if (pos==max_size)
			writebuf();
		buf[pos++]=c;
	}

	inline void FastOstream::put(char *t)
	{
		for (int i=0;t[i];++i)
			put(t[i]);
	}

	FastOstream::FastOstream(FILE *file=stdout,int maxBufSize=100000) : file(file) , pos(0) ,max_size(maxBufSize) , buf(0)
	{
		if (!file)
			throw exception("brak podanego pliku do utworzenia strumienia");
		buf = new char[max_size+2];
	}

	FastOstream::~FastOstream()
	{
		if (pos>0)
			writebuf();
		if (file)
			fclose(file);
		if (buf)
			delete[] buf;
	}


	inline FastOstream& FastOstream::operator<<(char c)
	{
		put(c);
		return *this;
	}

	inline FastOstream& FastOstream::operator<<(const char *t)
	{
		for (int i=0;t[i];++i)
			put(t[i]);
		return *this;
	}

	template<class T>
	inline FastOstream& FastOstream::operator<<(const T &v)
	{
		for (int i=itoa(v,ints);i>=0;--i)
			put(ints[i]);
		return *this;
	}

	template<class T>
	inline FastOstream& FastOstream::writeBinary(const T &v,int bytes)
	{
		while (--bytes>=0)
			put((v>>(8*bytes))&255);
		return *this;
	}

	template<class T>
	inline FastOstream& FastOstream::operator<<(const vector<T> &V)
	{
		writeBinary(V.size(),4);
		for (unsigned i=0;i<V.size();++i)
			writeBinary(V[i],sizeof(T));
		//for (unsigned i=0;i<V.size();++i)
		//	*this << i << ' ' << V[i] << '\n';
		return *this;
	}

	template<class T1,class T2>
	inline FastOstream& FastOstream::operator<<(const std::pair<T1,T2> &p)
	{
		*this << '(' << p.first << ',' << p.second << ')';
		return *this;
	}


	class FastIstream
	{
	private:
		const int max_size;
		char *buf;
		int buf_size,pos;
		int unbufferedRead;
		FILE *file;
		inline void getbuf();
	public:
		FastIstream(FILE*,int);
		~FastIstream();
		bool unbuffered;
		int good;
		operator bool();
		inline char getc();
		inline FastIstream& operator>>(char&);
		inline FastIstream& operator>>(char*);
		inline FastIstream& getLine(char*);
		template<class T>
		inline FastIstream& operator>>(T &v);
		template<class T>
		inline FastIstream& readBinary(T &v,int bytes);
		template<class T>
		inline FastIstream& operator>>(vector<T> &V);
		int fseek(long offset,int origin);
		unsigned int ftell() const;
	};

	FastIstream::FastIstream(FILE *file=stdin,int maxBufSize=100000) : file(file) , buf_size(0) , pos(0), good(2) , 
		max_size(maxBufSize) , buf(0) , unbuffered (false)
	{ 

		if (file==0)
		{
			Logger logger(stderr,LogLevel::Debug);
			logger.AddLog("log.txt",LogLevel::Info);
			logger.Log("brak pliku do utworzenia strumienia");
			throw exception("brak pliku do utworzenia strumienia");
		}
		buf = new char[max_size+2];
	}

	FastIstream::~FastIstream()
	{
		if (file)
			fclose(file);
		if (buf)
			delete[] buf;
	}



	inline void FastIstream::getbuf()
	{
		buf_size=fread(buf,1,max_size,file);
	}

	inline char FastIstream::getc()
	{
		if (pos>=buf_size)
		{
			if (good==1 || good==0 || unbuffered)
				return good=0;
			else if (feof(file))
			{
				good=1;
				return 0;
			}
			getbuf();
			pos=0;
		}
		return buf[pos++];
	}

	inline FastIstream::operator bool()
	{
		return good!=0 && (good!=3 || unbuffered==false);
	}

	inline FastIstream& FastIstream::operator >>(char &c)
	{
		c=getc();
		return *this;
	}

	inline FastIstream& FastIstream::operator >>(char *t)
	{
		char tmp;
		while ((tmp=getc()) && (tmp==' ' || tmp=='\n' || tmp=='\t'));
		do
		{
			*t=tmp;
			++t;
			tmp=getc();
		}
		while (tmp && tmp!=' ' && tmp!='\n' && tmp!='\t');
		*t=0;
		return *this;
	}

	inline FastIstream& FastIstream::getLine(char *t)
	{
		char tmp=getc();
		while (tmp && tmp!='\n')
		{
			*t=tmp;
			++t;
			tmp=getc();
		}
		*t=0;
		return *this;
	}

	template<class T>
	inline FastIstream& FastIstream::operator>>(T &v)
	{
		char tmp;
		while ((tmp=getc()) && (tmp==' ' || tmp=='\n' || tmp=='\t'));
		char ints[20],i=0;
		do
		{
			ints[i++]=tmp;
			tmp=getc();
		}
		while (tmp && tmp!=' ' && tmp!='\n' && tmp!='\t');
		ints[i]=0;
		v=(T)atoi(ints);
		return *this;
	}


	template<class T>
	inline FastIstream& FastIstream::readBinary(T &v,int bytes)
	{
		v=T(0);
		while (--bytes>=0)
			v|=(((T)(getc()&255))<<(8*bytes));
		return *this;
	}


	template<class T>
	inline FastIstream& FastIstream::operator>>(vector<T> &V)
	{
		unsigned size;
		readBinary(size,4);
		V.resize(size);
		for (unsigned i=0;i<size;++i)
			readBinary(V[i],sizeof(T));
		return *this;
	}


	int FastIstream::fseek(long offset, int origin)
	{
		if (origin == SEEK_SET)
		{
			int dif = offset - ftell();
			//fprintf(stderr,"offset = %d filepos = %d buf_pos = %d\n",offset,ftell(),pos);
			if (dif + pos >=0 && dif + pos < buf_size)
			{
				pos += dif;
				return 1;
			}
		}
		if (unbuffered)
			return good=0;
		buf_size=0;
		int streamState=::fseek(file,offset,origin);
		return 1;
	}

	unsigned int FastIstream::ftell() const
	{
		return ::ftell(file)-buf_size+pos;
	}


}









#endif
